package ai;

import game.Game;

public class AlphaBetaMultiThreaded extends AlphaBetaImproved
{
	//Set the maximum depth of recursion for the algorithm
	private final static int MAX_DEPTH = 10;
	
	//Stores the results form the different threads
	private int[] results;
	
	public AlphaBetaMultiThreaded(Game game, byte side)
	{
		super(game, side);
		
		this.results = new int[Game.XSIZE];
	}
	
	
	protected int getZet()
	{
		//Array to hold the sub threads
		Thread subThreads[] = new Thread[Game.XSIZE];
		
		//Initialize the results array
		resetResultsArray();
		
		//Create the sub threads
		for (int i = 0; i < Game.XSIZE; i++)
		{
			subThreads[i] = new Thread(new AiColumnThread(game, this.side, i, this, this.game.getPlayingField()) );
			subThreads[i].setName("AI Player: "+this.side+" Subthread: "+(i+1));
		}
		
		//Start the sub threads
		for (int i = 0; i < Game.XSIZE; i++)
		{
			subThreads[i].start();
		}
		
		//Wait for the sub threads to finished
		for (int i = 0; i < Game.XSIZE; i++)
		{
			if (subThreads[i].isAlive())
			{
				try
				{
					subThreads[i].join();
				}
				catch (InterruptedException e)
				{
					//Shouldn't happen
					e.printStackTrace();
				}
			}
		}
		
		//Calculate the bestmove
		int bestvalue = MINIMUM_VALUE;
		for (int i = 0; i < Game.XSIZE; i++)
		{
			//If the current bestvalue is worse then the value form this column then update the bestvalue
			if (bestvalue < this.results[i])
			{
				bestvalue = this.results[i];
				this.bestmove = i;
			}
		}
		
		//Return the calculated value 
		return bestmove;
		
	}
	
	//A function to set the results array back to the default
	private synchronized void resetResultsArray()
	{
		for (int i = 0; i < Game.XSIZE; i++)
		{
			this.results[i] = MINIMUM_VALUE;
		}
	}
	
	//The function used by the subthreads to report their calculated data
	public synchronized void setCalculatedValues(int column, int value)
	{
		this.results[column] = value;
		notifyAll();
	}
	
	
	private class AiColumnThread extends AlphaBetaImproved
	{
		//The column to start in
		private int column;

		//The parent thread
		private AlphaBetaMultiThreaded parentThread;
		
		public AiColumnThread(Game game, byte side, int column, AlphaBetaMultiThreaded parentThread, byte[][] aiPlayingField)
		{
			super(game, side);
			
			this.column = column;
			this.parentThread = parentThread;
			
			//Copy a new playing field to the ai-array.
			copyPlayingField(aiPlayingField);
		}
		
		public void run()
		{
			//Set the counter to zero
			//this.counter = 0;
			
			int replyValue = chooseMove( this.side, MINIMUM_VALUE, MAXIMUM_VALUE, 0 );
			
			//System.out.println("AlphaBetaImproved counter: "+this.counter);

			
			//if(replyValue <= MINIMUM_VALUE)
			//{
			//	System.out.println("AlphaBetaImproved giving up, bestmove: "+this.bestmove);
			//}
			
			//Send value to calling thread
			this.parentThread.setCalculatedValues(column, replyValue);
		}
		
		//Recursive function to determine the optimal move
		private int chooseMove(byte side, int alpha, int beta, int depth)
		{
			byte opponent;
			int value;
			int reply;
			
			//Base case
			int basecase = evaluateField(this.side);
			if (depth >= MAX_DEPTH || basecase == LOSE_VALUE || basecase == WIN_VALUE)
			{
				return basecase;
			}
			
			//Increment the counter
			this.counter++;
			
			if (side == this.side)
			{
				opponent = this.opponent;
				value = alpha;
			}
			else
			{
				opponent = this.side;
				value = beta;
			}
			
			
			//Main loop
			Outer:
			for (int i = 0; i < Game.XSIZE; i++)
			{
				//Only do your assigned column
				//i know goto's are ugly but i'm lazy at the moment, i'll clean this up later (maybe)
				if (depth == 0 && i != column)
				{
					continue;
				}
				
				if ( ! this.columnIsFull(i) )
				{
					placeToken(i, side);
					reply = chooseMove(opponent, alpha, beta, depth + 1);
					removeToken(i);
					
					//DEBUG:
					//if (depth == 0)
					//{
					//	System.out.println("Column: "+i+" has Strength: "+reply);
					//}
					
					//Update the return value if we've found a better posibility
					if (side == this.side && reply > value ||
							side == this.opponent && reply < value)
					{
						value = reply;
						bestmove = i;
						
						if( side == this.side)
							alpha = reply;
						else
							beta = reply;
						
						//Checking if it's a refutation, if so then skip node
						if (alpha >= beta)
							break Outer; // Refutation
					}
					
				}
			}
			
			//Only update the bestmove at the highest level (depth 0)
			return value;
		}
	}
	
}
